﻿// <copyright file="Link.cs" company="City of Portland, BES-ASM">
// </copyright>
// <summary>Link class</summary>

namespace BESASM.EMGAATS.BusinessClasses
{
  using System;
  using System.Collections.Generic;
  using System.Collections.ObjectModel;
  using System.Linq;
  using System.Text;
  using System.Threading.Tasks;

  /// <summary>
  /// Link class
  /// </summary>
  public class Link : Entity, ILink
  {
    private IPolyline geometry;
    private int name;
    private INode usNode;
    private INode dsNode;
    private double usIE;
    private double dsIE;
    private Material material;
    private ObservableCollection<string> asBuilts;
    private ICrossSection crossSection;
    private double heightIn;
    private double? lengthFt;

    /// <summary>
    /// Initializes a new instance of the <see cref="Link"/> class.
    /// </summary>
    /// <param name="id">The ID of the Link</param>
    public Link(int id)
      : base(EntityType.Link, id)
    {
      this.asBuilts = new ObservableCollection<string>();
      this.asBuilts.CollectionChanged += this.AsBuiltsChanged;
    }

    /// <summary>
    /// Gets or sets the linear geometry (planar) of the link.
    /// </summary>
    public IPolyline Geometry
    {
      get
      {
        return this.geometry;
      }

      set
      {
        if (value != this.geometry)
        {
          this.geometry = value;
          this.NotifyPropertyChanged();
        }
      }
    }

    /// <summary>
    /// Gets or sets the human readable link name 
    /// </summary>
    public int LinkName
    {
      get
      {
        return this.name;
      }

      set
      {
        if (value != this.name)
        {
          this.name = value;
          this.NotifyPropertyChanged();
        }
      }
    }

    /// <summary>
    /// Gets or sets the upstream node the link is connected to.
    /// </summary>
    public INode USNode 
    {
      get
      {
        return this.usNode;
      }

      set
      {
        if (value != this.usNode)
        {
          this.usNode = value;
          this.NotifyPropertyChanged();
        }
      }
    }

    /// <summary>
    /// Gets or sets the downstream node the link is connected to.
    /// </summary>
    public INode DSNode
    {
      get
      {
        return this.dsNode;
      }

      set
      {
        if (value != this.dsNode)
        {
          this.dsNode = value;
          this.NotifyPropertyChanged();
        }
      }
    }

    /// <summary>
    /// Gets or sets the upstream invert elevation of the link in feet.
    /// </summary>
    public double USIEFt
    {
      get
      {
        return this.usIE;
      }

      set
      {
        if (value != this.usIE)
        {
          this.usIE = value;
          this.NotifyPropertyChanged();
        }
      }
    }

    /// <summary>
    /// Gets or sets the downstream invert elevation of the link in feet.
    /// </summary>
    public double DSIEFt 
    {
      get
      {
        return this.dsIE;
      }

      set
      {
        if (value != this.dsIE)
        {
          this.dsIE = value;
          this.NotifyPropertyChanged();
        }
      }
    }

    /// <summary>
    /// Gets or sets the material of the link.
    /// </summary>
    public Material Material
    {
      get
      {
        return this.material;
      }

      set
      {
        if (value != this.material)
        {
          this.material = value;
          this.NotifyPropertyChanged();
        }
      }
    }

    /// <summary>
    /// Gets the collection of as-built diagram names associated with this link.
    /// </summary>
    public ICollection<string> AsBuilts
    {
      get
      {
        return this.asBuilts;
      }
    }

    /// <summary>
    /// Gets or sets the cross-section geometry of the link.
    /// </summary>
    /// <remarks>A null value indicates the link is circular.</remarks>
    public ICrossSection CrossSection
    {
      get
      {
        return this.crossSection;
      }

      set
      {
        if (value != this.crossSection)
        {
          this.crossSection = value;
          this.NotifyPropertyChanged();
        }
      }
    }

    /// <summary>
    /// Gets or sets the height of the link.
    /// </summary>
    public double HeightIn
    {
      get
      {
        return this.heightIn;
      }

      set
      {
        ////if (value <= 0.0)
        ////{
        ////  throw new ArgumentException("Link height must be a positive number.");
        ////}

        if (value != this.heightIn)
        {
          this.heightIn = value;
          this.NotifyPropertyChanged();
        }
      }
    }

    /// <summary>
    /// Gets the calculated cross-sectional area of the link.
    /// </summary>
    public double AreaSqIn
    {
      get
      {
        if (this.CrossSection != null)
        {
          return this.CrossSection.GetAreaSqIn(this.HeightIn);
        }
        else
        {
          double r = this.HeightIn / 2.0;
          return Math.PI * r * r;
        }
      }
    }

    /// <summary>
    /// Gets or sets the length of the link in feet.
    /// </summary>
    public double LengthFt
    {
      get
      {
        if (this.lengthFt.HasValue)
        {
          return this.lengthFt.Value;
        }
        else if (this.Geometry != null)
        {
          return this.Geometry.Length;
        }
        else
        {
          return double.NaN;
        }
      }

      set
      {
        if (double.IsNaN(value) && this.lengthFt.HasValue)
        {
          this.lengthFt = null;
          this.NotifyPropertyChanged();
        }
        else
        {
          ////if (value <= 0.0)
          ////{
          ////  throw new ArgumentException("Link length must be a positive number.");
          ////}

          if (!this.lengthFt.HasValue || value != this.lengthFt)
          {
            this.lengthFt = value;
            this.NotifyPropertyChanged();
          }
        }
      }
    }

    /// <summary>
    /// Creates a new entity that is a copy of the current instance.
    /// </summary>
    /// <returns>A new object that is a shallow copy of this instance.</returns>
    public override object Clone()
    {
      Link clone = new Link(this.Id);

      // Simple properties and references
      // DO NOT copy Network, clones must be added to a network
      clone.LinkName = this.LinkName;
      clone.CrossSection = this.CrossSection;
      clone.DSIEFt = this.DSIEFt;
      clone.DSNode = this.DSNode;
      clone.heightIn = this.heightIn;
      clone.Material = this.Material;
      clone.USIEFt = this.USIEFt;
      clone.USNode = this.USNode;

      // Geometry (copy)
      if (this.Geometry != null)
      {
        clone.Geometry = this.Geometry.Clone() as IPolyline;
      }

      // As Builts
      foreach (string asBuilt in this.AsBuilts)
      {
        clone.AsBuilts.Add(asBuilt);
      }

      // Copy internal length, not property which is calculated
      clone.lengthFt = this.lengthFt;

      this.CloneAuditProperties(clone);

      clone.SetModified(false);
      return clone;
    }

    /// <summary>
    /// Returns a string representation of this link.
    /// </summary>
    /// <returns>A string representation of this link.</returns>
    public override string ToString()
    {
      return string.Format("L{0} ({1}) N{2}-N{3}", this.Id, this.LinkName, this.USNode == null ? 0 : this.USNode.Id, this.DSNode == null ? 0 : this.DSNode.Id);
    }

    private void AsBuiltsChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
      this.NotifyPropertyChanged("AsBuilts");
    }
  }
}
